package nachos.threads;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;

import nachos.machine.*;

/**
 * Uses the hardware timer to provide preemption, and to allow threads to sleep
 * until a certain time.
 */
public class Alarm {
    /**
     * Allocate a new Alarm. Set the machine's timer interrupt handler to this
     * alarm's callback.
     *
     * <p><b>Note</b>: Nachos will not function correctly with more than one
     * alarm.
     */
    public Alarm() {
	Machine.timer().setInterruptHandler(new Runnable() {
		public void run() { timerInterrupt(); }
	    });
		this.alarmList2 = new HashMap<Long, KThread>();
		this.savedVar = new alarmVariables();
		this.alarmTimer = new LinkedList<Long>();
    }

    /**
     * The timer interrupt handler. This is called by the machine's timer
     * periodically (approximately every 500 clock ticks). Causes the current
     * thread to yield, forcing a context switch if there is another thread
     * that should be run.
     */
    public void timerInterrupt() {
	KThread.currentThread().yield();
    	boolean intStatus = Machine.interrupt().disable();
    	int min = 0; boolean remove;
    	remove = false;
    	long lowestTimecheck = Machine.timer().getTime();
    	//System.out.println(alarmTimer.size() + " SIZE IS");
		if(!alarmTimer.isEmpty()){
			for(int i = 0; i < alarmTimer.size(); i++){
				//System.out.println(alarmTimer.get(i) + " A BUNCH " + lowestTimecheck + " " + i + " " + min);
					if(alarmTimer.get(i) <= lowestTimecheck){
						min = i;
						//System.out.println(alarmTimer.get(i) + " smaller.");
						remove = true;
					}
					if(remove){
						KThread removedThread = alarmList2.remove(alarmTimer.remove(min));
						remove = false;
						if(removedThread != null){
							removedThread.ready();}
					}
			}
		}
			/*
			if(remove){
				minLoopcheck = alarmlist.remove(min);
				System.out.println(minLoopcheck.alarmTimer + " removed.");
				if(minLoopcheck.alarmThread != null){
					minLoopcheck.alarmThread.ready();}
				remove = false;
				minLoopcheck.alarmTimer = Machine.timer().getTime();
				//timerInterrupt();
			}*/	
		/*
		while((alarmlist.size() > 0) && alarmlist.get(min).alarmTimer <= Machine.timer().getTime()){
			if(remove){
				System.out.println(alarmlist.get(min).alarmTimer+ " removed.");
				minLoopcheck = alarmlist.remove(min);
				if(minLoopcheck.alarmThread != null){
					minLoopcheck.alarmThread.ready();}
				remove = false;
			}
			if(!alarmlist.isEmpty()){
				minLoopcheck.alarmTimer = Machine.timer().getTime();
				for(int i = 0; i < alarmlist.size(); i++){
					if(alarmlist.get(i).alarmTimer <= minLoopcheck.alarmTimer){
						min = i;
						minLoopcheck.alarmTimer = alarmlist.get(i).alarmTimer;
						remove = true;
						System.out.println(alarmlist.get(i).alarmTimer);
					}
				}
			}
		
			
			System.out.println("DO I EVER GET HERE? " + alarmlist.size() +" SIZE IS");
		
		}
		*/
		//-----
		/*
		if(!alarmlist.isEmpty()){
			for(int i = 0; i < alarmlist.size(); i++){
					if(alarmlist.get(i).alarmTimer <= minLoopcheck.alarmTimer){
						if(alarmlist.get(i).alarmThread != null)
							minLoopcheck = alarmlist.get(i);
						min = i;
						remove = true;
					}
			}
				if(alarmlist.get(i).alarmTimer <= Machine.timer().getTime()){	
					alarmCheck = alarmlist.remove(i);
					if(alarmCheck.alarmThread != null)
						alarmCheck.alarmThread.ready();
			}
		}*/
			
		Machine.interrupt().restore(intStatus);
    }

    /**
     * Put the current thread to sleep for at least <i>x</i> ticks,
     * waking it up in the timer interrupt handler. The thread must be
     * woken up (placed in the scheduler ready set) during the first timer
     * interrupt where
     *
     * <p><blockquote>
     * (current time) >= (WaitUntil called time)+(x)
     * </blockquote>
     *
     * @param	x	the minimum number of clock ticks to wait.
     *
     * @see	nachos.machine.Timer#getTime()
     */
    public void waitUntil(long x) {
	// for now, cheat just to get something working (busy waiting is bad)
    long wakeTime = Machine.timer().getTime() + x;
    boolean intStatus = Machine.interrupt().disable();
    		
    
    		alarmList2.put(wakeTime, KThread.currentThread());
    		alarmTimer.add(wakeTime);
    		/*
			this.savedVar.alarmThread = KThread.currentThread();
    		System.out.println("ENTERING waituntil " + wakeTime);
    		this.savedVar.alarmTimer = wakeTime;
			alarmlist.add(savedVar);*/
    		
			KThread.currentThread().sleep();
			Machine.interrupt().restore(intStatus);
    }
    
    HashMap<Long, KThread> alarmList2;
    LinkedList<Long> alarmTimer;
    alarmVariables savedVar;
    
    protected class alarmVariables{
    	KThread alarmThread;
    	long alarmTimer;
    }
}

